import MagicString from "magic-string";
import path from 'path';
import {parse} from 'acorn';
import analyse from './analyse.js';
//每个文件都是一个模块，

function hasOwnProperty(obj,name){
  return Object.prototype.hasOwnProperty.call(obj,name);
  // return obj.hasOwnProperty(name);
}
const SYSTEM_VARIABLE = ['console', 'log','require','module','exports','constructor','replace','Error']
export default class Module{
  constructor({code,path,bundle}){
    this.code = new MagicString(code,{filename:path});
    this.path = path;
    this.bundle = bundle;
    this.ast = parse(this.code,{
      ecmaVersion:7,
      sourceType:'module'
    })
    this.imports = {};
    this.exports = {};
    //当前模块定义的变量
    this.definitions = {};
    //修改的变量
    this.modifications = {}
    this._analyse();
  }
  _analyse(){
    //拿到导入的变量和导出的变量
    this.ast.body.forEach(node =>{
      if(node.type ==='ImportDeclaration'){
        let source = node.source.value;  // ./msg
        let specifiers = node.specifiers;
        specifiers.forEach(spe=>{
          const name = spe.local.name;
          const localName = spe.local.name;
          //this.imports.name = {name:'na',source:'./msg'}
          this.imports[localName] = {name,localName,source}
          if(this.imports[localName].source[0] !=='.'){
            let bool = false;
            node._source = this.code.snip(node.start,node.end);
            this.bundle.deps.forEach(state =>{
              if(state.source.value === source){
                if(name === state.specifiers[0].local.name){
                  bool = true;
                }
                
              }
            })
            if (!bool)this.bundle.deps.push(node)
          }
          
        })
      }else if(node.type === 'ExportNamedDeclaration'){
        if(node.declaration && node.declaration.type === 'VariableDeclaration'){
          let declaration = node.declaration;
          // var dd
          let name = declaration.declarations[0].id.name;
          // this.exports.name = {node,name,expression}
          this.exports[name] = {
            node,localName:name,expression:declaration
          }
        }else if(node.specifiers){
          const specifiersNode = node.specifiers;
          specifiersNode.forEach(item=>{
            let name = item.local.name
            this.exports[name] = {
              node,localName:name,name,expression:item
            }
          })
        }
      }else if(node.type === 'ExportDefaultDeclaration'){
        let name
        let declaration = node.declaration;
        //class
        if(declaration && declaration.type ==='ClassDeclaration'){
          name = declaration.id.name;
        }else{
          name = declaration.name;
        }
        // function
        if(declaration && declaration.type ==='FunctionDeclaration'){
          name = declaration.id.name;
        }
        this.exports[name] = {
          node,localName:name,expression:declaration
        }
      }
    })
    analyse(this.ast,this.code,this);
    //当前模块声明的变量 不包括函数内
    
    this.ast.body.forEach(statement=>{
      Object.keys(statement._defines).forEach((name)=>{
        this.definitions[name] = statement
      });

      Object.keys(statement._modifies).forEach(name=>{
        if(!hasOwnProperty(this.modifications,name)){
          this.modifications[name]= [];
          this.modifications[name].push(statement)
        }
        
      })
    })
  }
  //取出定义的变量语句
  expandAllStatements(){
    let allStatements = [];
    this.ast.body.forEach(statement => {
      
      if(statement.type === 'ImportDeclaration'){
        return;
      }
      let statements = this.expandStatements(statement);
      
      allStatements.push(...statements)
    });
    return allStatements;
  }
  expandStatements(statement){
    // if(statement.type ==='ExpressionStatement'){console.log(statement);}
    
    let result = [];
    /**
     * [name,age]
     * 把当前代码块依赖的import变量写入result
     * export var name1 = "na"
     */
    
    const dependencies = Object.keys(statement._dependsOn);
    // console.log(dependencies,statement._code.toString());
    
    dependencies.forEach(name=>{
      let statement_deinition = this.define(name,statement);
      result.push(...statement_deinition);
    })


    const funDefine = Object.keys(statement._funDefine);
    // console.log(statement);
    funDefine.forEach(name=>{
      let statement_funDefine = this.define(name,statement);
      // if(statement ===statement_funDefine)
      result.push(...statement_funDefine);
    })
    /**
     * 把自己也写入result
     * 最终结果
     * export var name1 = "na"
     * function say(money,grid){
        console.log('hello',name1);
        }
     * 
     */
    if(statement._included === false){
      statement._included = true;
      result.push(statement);
    }

    //modifica
    const defines = Object.keys(statement._defines)
    // console.log(this.bundle);
    defines.forEach(name => {
      const modifications = hasOwnProperty(this.modifications, name) && this.modifications[name]
      
      if (modifications) {
        modifications.forEach(modif => {
          if (!modif._included) {
            const statements = this.expandStatements(modif)
            result.push(...statements)
          }
        })
      }
    })
    
    return result;
  }

  //找到定义这个变量的声明节点
  define(name,orgStatement){
    // console.log(this.definitions[name]);
    
    // if(name ==='walk')console.log(this.definitions);
    
    if(hasOwnProperty(this.imports,name)){
      // console.log(this.imports[name]['name']);
      
       //this.imports.name = {name:'na',source:'./msg'}
      const importData = this.imports[name];
      //处理node_module依赖
      if(importData.source[0] !== '.')return []
      let module
      // msg模块
      if(this.bundle.modules[importData.source]){
        module = this.bundle.modules[importData.source]
      }else{
        module = this.bundle.fetchModule(importData.source,this.path);
      }
      
      const exportData = module.exports[importData.name];
      
      if(exportData){
        return module.define(exportData.localName,orgStatement);
      }else throw new Error(`导入失败 ${name}`)
      
    }else{
      let statement = this.definitions[name];
      // if(name ==='age')console.log(this.code.toString(),statement,this.definitions);
      if(statement){
        if(statement._included === false){
          if(statement === orgStatement){
            return []
          }else return this.expandStatements(statement);
        }else{
          return [];
        }
        
      }else if (SYSTEM_VARIABLE.includes(name)){
        return []
      } else {
        // console.log(name,this.code.toString());
        throw new Error(`variable '${name}' is not exist1`)
      }
      

    }
  }
}